package ysq.xyg.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.github.pagehelper.StringUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;
import ysq.weixin.util.SpringApplicationContextUtil;
import ysq.xyg.dao.WinNumDao;
import ysq.xyg.model.*;
import ysq.xyg.service.*;
import ysq.xyg.util.BaseConstant;
import ysq.xyg.util.BaseUtil;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.*;

/**
 * @author yu sheng qiang
 * @create 2017-04-19 14:11
 **/
@Service("winNumService")
public class WinNumServiceImpl extends BaseServiceImpl<WinNum> implements WinNumService {

    @Resource
    private ActivityService activityService;
    @Resource
    private ReceivingService receivingService;
    @Resource
    private RecordService recordService;
    @Resource
    private MemberService memberService;
    @Resource
    private ItemService itemService;
    @Resource
    private WinNumDao winNumDao;
    @Autowired
    public void setWinNumDao(WinNumDao winNumDao) {
        this.baseDao = winNumDao;
    }

    private static ConcurrentMap<String, List<WinNum>> winNumMap = new ConcurrentHashMap<String, List<WinNum>>();
    private static ConcurrentMap<String, Boolean> flagMap = new ConcurrentHashMap<String, Boolean>();

    @Override
    public PageInfo<WinNum> pageInfoLeft(WinNum winNum, int pageSize, int pageNum){
        PageHelper.startPage(pageNum,pageSize);
        if(StringUtil.isNotEmpty(winNum.getNickName())){
            winNum.setNickName(BaseUtil.encodeBase64String(winNum.getNickName()));
        }
        List<WinNum> list=winNumDao.selectEntityLeft(winNum);
        return new PageInfo<WinNum>(list);
    }

    @Override
    public int generateWinNum(int totalNum, String activityId) {
        int flag = 0;
        if (totalNum > 0) {
            List<WinNum> list = new ArrayList<WinNum>();
            for (int i = 1; i <= totalNum; i++) {
                int num = 10000000 + i;
                WinNum winNum = new WinNum();
                winNum.setId(BaseUtil.uId());
                winNum.setActivityId(activityId);
                winNum.setNum(num);
                winNum.setState(BaseConstant.ACTIVITY_WINNUM_NO);
                winNum.setCreateTime(new Date());
                list.add(winNum);
            }
            winNumDao.insertEntityList(list);
            flag = totalNum;
        }
        return flag;
    }

    @Transactional(rollbackFor = {RuntimeException.class})
    public int winningNumOld(String memberId, String activityId, int num) {
        // 需要判断是不是最后一个中奖号
        int flag = 0;
        if (num > 0) {
            int e = 0;
            for (int i = 0; i < num; i++) {
                e = winNumDao.updateByNum(memberId, activityId);
                if (e <= 0) {
                    throw new RuntimeException( "Dao的时候手动抛出异常" );
                }
            }
            // 获取id
            flag = num;
        }
        return flag;
    }

    @Override
    @Transactional(rollbackFor = {RuntimeException.class})
    public int winningNum(String memberId, String activityId, int num,String itemId) {
        Activity activity=activityService.selectByPrimaryKey(activityId);
        Item item=itemService.selectByPrimaryKey(itemId);
        // 存放记录
        Record record = new Record();
        record.setId(BaseUtil.uId());
        record.setMemberId(memberId);
        record.setDescripts("购买商品("+item.getTitle()+"[第"+activity.getPeriodsNum()+"期])消耗"+num+"个钻石");
        record.setMoney(num);
        record.setType(BaseConstant.RECORD_TYPE_XFJL);
        record.setExtraId(activityId);
        record.setCreateTime(new Date());

        // 修改个人钻石
        Member member = memberService.selectByPrimaryKey(memberId);
        if (member == null || member.getMoney() < num) {
            return -1;
        }
        // 需要判断是不是最后一个中奖号
        int flag = 0;
        Boolean flagBoolean = flagMap.get(activityId);
        if (flagBoolean != null && flagBoolean) {
            return flag;
        }
        List<WinNum> winNumList = winNumMap.get(activityId);
        if (winNumList == null || winNumList.size() == 0) {
            // 查询出来放入
            winNumList = winNumDao.selectEntityNoNum(activityId);
            winNumMap.put(activityId, winNumList);
        }
        // 奖金号码数量
        int numSize = (int)(num / item.getPrice());
        if (winNumList.size() < numSize) {
            return -2;
        }
        if (num > 0 && winNumList != null && winNumList.size() > 0 && winNumList.size() >= numSize) {
            // 存放即将改变的中间号
            List<String> recordList = new ArrayList<String>();
            Iterator<WinNum> iterator = winNumList.iterator();
            int i = 0;
            while (iterator.hasNext()) {
                if (i >= numSize) {
                    break;
                }
                recordList.add(iterator.next().getId());
                iterator.remove();
                i++;
            }
            try {
                if (recordList != null && recordList.size() > 0) {
                    member.setMoney(member.getMoney() - num);
                    memberService.updateByPrimaryKey(member);
                    recordService.insert(record);
                    winNumDao.updateList(memberId, activityId, recordList);
                }
            } catch (RuntimeException e) {
                throw new RuntimeException();
            }
            if (winNumList == null || winNumList.size() == 0) {
                this.countWinNum(activityId);
            }
            // 获取id
            flag = num;
        }
        return flag;
    }

    @Override
    public long selectCountNoMember(String activityId) {
        return winNumDao.selectCountNoMember(activityId);
    }

    @Override
    @Transactional(rollbackFor = {RuntimeException.class})
    public void randOneWinNum(String activityId) {
        try {
            long winCount = this.selectCountWin(activityId);
            if (winCount != 0) {
                return;
            }
            // 处理中奖号,处理winNum表状态,为中奖人生成订单号
            Activity activity = activityService.selectByPrimaryKey(activityId);
            WinNum winNum = null;
            if (StringUtils.isNotEmpty(activity.getDefaultNum())) {
                WinNum record = new WinNum();
                record.setActivityId(activityId);
                record.setNum(Integer.parseInt(activity.getDefaultNum()));
                winNum = winNumDao.selectOne(record);
            } else {
                // 处理,选出中奖号,使用sql语句中order by rand() LIMIT 1
                winNum = winNumDao.selectRandOneWinNum(activityId);
            }
            activity.setStatus(BaseConstant.ACTIVITY_ED);
            activity.setUpdateTime(new Date());
            activity.setWinningNum(String.valueOf(winNum.getNum()));
            activityService.updateByPrimaryKeySelective(activity);
            winNum.setState(BaseConstant.ACTIVITY_WINNUM_YES);
            winNumDao.updateByPrimaryKey(winNum);
            // 为中奖人生成
            Receiving receiving = new Receiving();
            receiving.setId(BaseUtil.uId());
            receiving.setMemberId(winNum.getMemberId());
            receiving.setStatus(BaseConstant.ACTIVITY_ING);
            receiving.setActivityId(activityId);
            receiving.setItemId(activity.getItemId());
            receiving.setCreateTime(new Date());
            receivingService.insert(receiving);
            // 是否自动开启下一期
            if (StringUtils.equals(activity.getIsAuto(), BaseConstant.ITEM_ACT_AUTO_YSE)) {
                Activity newActivity = new Activity();
                int periodsNum = activity.getPeriodsNum() + 1;
                String newActivityId = BaseUtil.uId();
                newActivity.setId(newActivityId);
                newActivity.setItemId(activity.getItemId());
                newActivity.setStatus(BaseConstant.ACTIVITY_ING);
                newActivity.setTitle("第" + periodsNum + "期");
                newActivity.setTotalNum(activity.getTotalNum());
                newActivity.setPeriodsNum(periodsNum);
                newActivity.setIsAuto(BaseConstant.ITEM_ACT_AUTO_YSE);
                newActivity.setCreateTime(new Date());
                activityService.insert(newActivity);
                // 生成活动号
                this.generateWinNum(activity.getTotalNum(), newActivityId);
            }
        } catch (RuntimeException e) {
            throw new RuntimeException();
        }
    }

    @Override
    public long selectCountWin(String activityId) {
        return winNumDao.selectCountWin(activityId);
    }

    @Override
    public List<WinNum> selectEntity(WinNum record) {
        return winNumDao.selectEntity(record);
    }

    @Override
    public int defaultWinningNum(String activityId, String winningNum) {
        Activity activity = new Activity();
        activity.setId(activityId);
        activity.setDefaultNum(winningNum);
        return activityService.updateByPrimaryKeySelective(activity);
    }

    //分页查询
    @Override
    public PageInfo<WinNum> pageInfo(WinNum winNum, int pageSize, int pageNum){
        PageHelper.startPage(pageNum,pageSize);
        //通用Example查询
        Example example = new Example(WinNum.class);
        Example.Criteria criteria = example.createCriteria();
        if(winNum.getNum()>0){
            criteria.andEqualTo("num",winNum.getNum());
        }
        if(StringUtil.isNotEmpty(winNum.getActivityId())){
            criteria.andEqualTo("activity_id",winNum.getActivityId());
        }
        if(StringUtil.isNotEmpty(winNum.getNickName())){
            criteria.andLike("nickName", "%"+ BaseUtil.encodeBase64String(winNum.getNickName())+"%");
        }
        if(StringUtil.isNotEmpty(winNum.getMemberId())){
            criteria.andLike("member_id","%"+winNum.getMemberId()+"%");
        }
        if(StringUtil.isNotEmpty(winNum.getPhone())){
            criteria.andLike("phone","%"+winNum.getPhone()+"%");
        }
        List<WinNum> list=winNumDao.selectByExample(example);
        return new PageInfo<WinNum>(list);
    }

    //条件查询
    @Override
    public List<WinNum> list(WinNum winNum){

        Example example = new Example(WinNum.class);
        Example.Criteria criteria = example.createCriteria();
        if(winNum.getNum()>0){
            criteria.andEqualTo("num",winNum.getNum());
        }
        if(StringUtil.isNotEmpty(winNum.getActivityId())){
            criteria.andEqualTo("activity_id",winNum.getActivityId());
        }
        if(StringUtil.isNotEmpty(winNum.getNickName())){
            criteria.andLike("nickName", "%"+ BaseUtil.encodeBase64String(winNum.getNickName())+"%");
        }
        if(StringUtil.isNotEmpty(winNum.getMemberId())){
            criteria.andLike("member_id","%"+winNum.getMemberId()+"%");
        }
        if(StringUtil.isNotEmpty(winNum.getPhone())){
            criteria.andLike("phone","%"+winNum.getPhone()+"%");
        }
        List<WinNum> list=winNumDao.selectByExample(example);
        return list;
    }

    /**
     * 锁住,处理中奖
     */
    private synchronized void countWinNum(String activityId){
        Boolean flag = flagMap.get(activityId);
        if (flag != null && flag) {
            return;
        }
        flagMap.put(activityId, true);
        // 线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<Runnable>(5));
        // 启动线程，使用线程代替
        WinNumRand myTask = new WinNumRand(activityId);
        executor.execute(myTask);
        executor.shutdown();
        // this.randOneWinNum(activityId);
    }
}

class WinNumRand implements Runnable {

    private String activityId;

    public WinNumRand(String activityId) {
        this.activityId = activityId;
    }

    @Override
    public void run() {
        WinNumService winNumService = (WinNumService) SpringApplicationContextUtil.getBean("winNumService");
        winNumService.randOneWinNum(activityId);
    }
}
